﻿using System;
using System.Collections.Generic;
using System.Linq;
using AIPASoft.DataAccessor;
using AIPASoft.Entities.Security;
using AIPASoft.Interfaces;
using AIPASoft.Interfaces.Base;
using AIPASoft.Interfaces.Security;

namespace AIPASoft.DataProvider
{
    public class SecurityDataProvider : ISecurityDataProvider, IPlugin
    {

        #region Public Properties


        public SecDataAccessorDataContext DBSecDataContext { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Public constructor
        /// </summary>
        public SecurityDataProvider()
        {
            DBSecDataContext = new SecDataAccessorDataContext();
            Name = "SecurityDataProvider";
        }

        #endregion

        #region ISecurityDataProvider

        #region Factories

        #region Public

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IUser CreateNewUser()
        {
            return new UserBase();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IRole CreateNewRole()
        {
            return new RoleBase();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IPermission CreateNewPermission()
        {
            return new PermissionBase();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IAuthorisation CreateNewAuthorisation()
        {
            return new AuthorisationBase();
        }
      
        #endregion

        #region Private
        /// <summary>
        /// This function maps the DB entity of User table to application entity IUser 
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        private IUser ConvertToUserEntity(User user)
        {
            if(user==null)
                return null;

            IUser userEntity = CreateNewUser();
            userEntity.Department = user.Department;
            userEntity.Designation = user.Designation;
            userEntity.Email = user.Email;
            userEntity.FullName = user.FullName;
            userEntity.IsWindowsUser = user.IsWindowsUser;
            userEntity.Password = user.Password;
            userEntity.Tel = user.Tel;
            userEntity.UserID = user.ID;
            userEntity.UserName = user.Username;
            userEntity.IsDeleted = user.IsDeleted;
            userEntity.RoleID = user.RoleID;
            userEntity.CreatedBy = user.CreatedBy;
            userEntity.CreatedOn = user.CreatedOn;

            return userEntity;
        }

        /// <summary>
        /// This function maps the DB entity of Role table to the application entity IRole
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        private IRole ConvertToRoleEntity(Role role)
        {
            if (role == null)
                return null;

            IRole roleEntity = CreateNewRole();
            roleEntity.RoleId = role.ID;
            roleEntity.RoleName = role.RoleName;
            return roleEntity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="permission"></param>
        /// <returns></returns>
        private IPermission ConvertToPermissionEntity(Permission permission)
        {
            if (permission == null)
                return null;

            IPermission permissionEntity = CreateNewPermission();
            permissionEntity.PermissionID = permission.ID;
            permissionEntity.PermissionName = permission.PermissionName;
            return permissionEntity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="authorisation"></param>
        /// <returns></returns>
        private IAuthorisation ConvertToAuthorisationEntity(Authorisation authorisation)
        {
            if (authorisation == null)
                return null;

            IAuthorisation authorisationEntity = CreateNewAuthorisation();
            authorisationEntity.PermissionID = authorisation.PermissionID;
            authorisationEntity.RoleID = authorisation.RoleID;
            return authorisationEntity;
        }


        private User ConvertToUserDBEntity(IUser user)
        {
            if (user == null)
                return null;

            var userDBEntity = new User
                {
                    Department = user.Department,
                    Designation = user.Designation,
                    Email = user.Email,
                    FullName = user.FullName,
                    IsWindowsUser = user.IsWindowsUser,
                    Password = user.Password,
                    Tel = user.Tel,
                    ID = user.UserID,
                    Username = user.UserName,
                    IsDeleted = user.IsDeleted,
                    RoleID = user.RoleID,
                    CreatedBy = user.CreatedBy,
                    CreatedOn = user.CreatedOn,
                    Role = ConvertToRoleDBEntity(user.RoleID)
                };

            return userDBEntity;
        }

        private Role ConvertToRoleDBEntity(int roleID)
        {
            Role res = (from rec in DBSecDataContext.Roles.ToList() where rec.ID == roleID select rec).FirstOrDefault();
            return res;
        }

        private Permission ConvertToPermissionDBEntity(IPermission permission)
        {
            if (permission == null)
                return null;

            Permission res =
                (from rec in DBSecDataContext.Permissions.ToList()
                 where rec.PermissionName == permission.PermissionName
                 select rec).FirstOrDefault();
            return res;
        }

        private Authorisation ConvertToAuthorisationDBEntity(IAuthorisation authorisation)
        {
            if (authorisation == null)
                return null;

            Authorisation res =
                (from rec in DBSecDataContext.Authorisations.ToList() where rec.ID == authorisation.ID select rec).
                    FirstOrDefault();

            return res;
        }

        #endregion

        #endregion

        #region Add Entities

        /// <summary>
        /// Add a new User
        /// </summary>
        /// <param name="user"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult AddUser(IUser user, out string result)
        {
            DataProviderOperationResult res;
            result = "";

            IUser resUser = FindUser(user.UserID);

            if (resUser == null)
            {
                User userDBEntity = ConvertToUserDBEntity(user);
                DBSecDataContext.Users.InsertOnSubmit(userDBEntity);
                DBSecDataContext.SubmitChanges();
                result = "User : " + user.UserName + " added successfully";
                res = DataProviderOperationResult.Success;
            }
            else
            {
                resUser.IsDeleted = true;
                DBSecDataContext.SubmitChanges();
                res = DataProviderOperationResult.Success;
            }

            return res;
        }

        /// <summary>
        /// Add a new Role
        /// </summary>
        /// <param name="role">Application Role entity</param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult AddRole(IRole role, out string result)
        {
            DataProviderOperationResult res;

            IRole resRole = FindRole(role.RoleName);

            if (resRole == null)
            {
                var roleDBEntity = new Role {RoleName = role.RoleName};
                DBSecDataContext.Roles.InsertOnSubmit(roleDBEntity);
                try
                {
                    DBSecDataContext.SubmitChanges();
                }
                catch (Exception exception)
                {
                    result = "Exception : " + exception.Message;
                    return DataProviderOperationResult.InValidDBStateError;
                }
                result = "Role : " + role.RoleName + " added successfully";
                res = DataProviderOperationResult.Success;
            }
            else
            {
                result = "Role : " + role.RoleName + " already exists";
                return DataProviderOperationResult.RecordAlreadyExistsError;
            }

            return res;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="permission"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult AddPermission(IPermission permission, out string result)
        {
            DataProviderOperationResult res;

            IPermission resPermission = FindPermission(permission.PermissionName);

            if (resPermission == null)
            {
                var permissionDBEntity = new Permission {PermissionName = permission.PermissionName};
                DBSecDataContext.Permissions.InsertOnSubmit(permissionDBEntity);
                try
                {
                    DBSecDataContext.SubmitChanges();
                }
                catch (Exception exception)
                {
                    result = "Exception : " + exception.Message;
                    return DataProviderOperationResult.InValidDBStateError;
                }
                result = "Permission : " + permission.PermissionName + " added successfully";
                res = DataProviderOperationResult.Success;
            }
            else
            {
                result = "Permission : " + permission.PermissionName + " already exists";
                return DataProviderOperationResult.RecordAlreadyExistsError;
            }

            return res;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="role"></param>
        /// <param name="permission"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult AddAuthorisation(IRole role, IPermission permission, out string result)
        {
            var res = DataProviderOperationResult.UnknownError;
            result = "";

            var resPermission = permission;
            var resRole = role;

            if (resPermission != null && resRole != null)
            {
                var roleDBEntity = ConvertToRoleDBEntity(resRole.RoleId);

                bool authorisationExists =
                    roleDBEntity.Authorisations.Any(
                        authorisation => authorisation.PermissionID == resPermission.PermissionID);

                if (!authorisationExists)
                {
                    var authorisationEntity = new Authorisation
                        {PermissionID = resPermission.PermissionID, RoleID = resRole.RoleId};

                    DBSecDataContext.Authorisations.InsertOnSubmit(authorisationEntity);

                    try
                    {
                        DBSecDataContext.SubmitChanges();
                    }
                    catch (Exception exception)
                    {
                        result = "Exception : " + exception.Message;
                        return DataProviderOperationResult.InValidDBStateError;
                    }

                    result = "Authorization : " + role.RoleName + " is mapped with " + permission.PermissionName +
                             " successfully";
                    res = DataProviderOperationResult.Success;
                }
                else
                {
                    result = "Authorization : " + role.RoleName + " already has a mapping with " +
                             permission.PermissionName;
                    return DataProviderOperationResult.RecordAlreadyExistsError;
                }
            }

            return res;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="roleID"></param>
        /// <param name="permissionID"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult AddAuthorisation(int roleID, int permissionID, out string result)
        {
            var roleEntity = FindRole(roleID);
            var permissionEntity = FindPermission(permissionID);

            return AddAuthorisation(roleEntity, permissionEntity, out result);
        }

        #endregion

        #region Update Delete Entities
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="user"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public DataProviderOperationResult DeleteUser(IUser user, out string result)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="role"></param>
        /// <param name="result"></param>
        /// <param name="removeDependencies"></param>
        /// <returns></returns>
        public DataProviderOperationResult DeleteRole(IRole role, out string result, bool removeDependencies = false)
        {
            var res = DataProviderOperationResult.UnknownError;
            result = "";

            Role resRole =
                (from rec in DBSecDataContext.Roles.ToList() where rec.RoleName == role.RoleName select rec).FirstOrDefault();

            if (resRole != null)
            {
                if (resRole.Users.Count > 0 && !removeDependencies)
                {
                    result = resRole.Users.Aggregate("Dependency records exists.",
                                                     (current, usr) =>
                                                     current + (usr.Username + Environment.NewLine));
                    res = DataProviderOperationResult.DependencyRecordError;
                }

                if (resRole.Users.Count > 0 && removeDependencies)
                {
                    foreach (User usr in resRole.Users)
                    {
                        SetDefaultRole(usr);
                    }
                }


                if (removeDependencies)
                {
                    resRole.IsDeleted = true;
                    DBSecDataContext.SubmitChanges();
                    result = "Role : " + role.RoleName + " deleted successfully";
                    res = DataProviderOperationResult.Success;
                }
            }
            return res;
        }

        public DataProviderOperationResult DeleteRole(string roleName, out string result)
        {
            var roleEntity =
                (from rec in DBSecDataContext.Roles.ToList() where rec.RoleName == roleName select rec).FirstOrDefault();

            if (roleEntity != null)
            {
                var roleDBEntity = ConvertToRoleEntity(roleEntity);
                return DeleteRole(roleDBEntity, out result);
            }

            result = "Role : " + roleName + " not found";
            return DataProviderOperationResult.RecordNotFoundError;
        }

        public DataProviderOperationResult DeleteRole(int roleID, out string result)
        {
            var roleEntity =
                (from rec in DBSecDataContext.Roles.ToList() where rec.ID == roleID select rec).FirstOrDefault();

            if (roleEntity != null)
            {
                var roleDBEntity = ConvertToRoleEntity(roleEntity);
                return DeleteRole(roleDBEntity, out result);
            }

            result = "Role : for the roleID " + roleID + " not found";
            return DataProviderOperationResult.RecordNotFoundError;
        }

        public DataProviderOperationResult DeletePermission(string permissionName, out string result)
        {
            var dependencyDeleteSuccess = true;

            var permissionDBEntity =
                (from rec in DBSecDataContext.Permissions.ToList() where rec.PermissionName == permissionName select rec).
                    FirstOrDefault();

            if(permissionDBEntity!=null)
            {
                var delLog = "";
                var  authDeleteRes = DataProviderOperationResult.Success;


                var authorisations = GetAllAuthorisations().TakeWhile(x=>x.PermissionID==permissionDBEntity.ID).ToList();
                foreach (var authorisation in authorisations)
                {
                    string temp;
                    try
                    {
                        authDeleteRes = DeleteAuthorisation(authorisation.ID, out temp);

                        if (authDeleteRes != DataProviderOperationResult.Success)
                        {
                            dependencyDeleteSuccess = false;
                            break;
                        }
                    }
                    catch (Exception exception)
                    {
                        result = "Exception occured while deleting authorisation ID : " + authorisation.ID + Environment.NewLine + exception.Message;
                        return DataProviderOperationResult.UnknownError;
                    }
                    
                    delLog += temp + Environment.NewLine;
                }


                result = delLog;

                if (dependencyDeleteSuccess)
                { 
                    result += "Permission : " + permissionDBEntity.PermissionName + " deleted successfully";
                    return DataProviderOperationResult.Success;
                }
                else
                {
                    result += "Permission : " + permissionDBEntity.PermissionName + " could not be deleted successfully";
                    return authDeleteRes;
                }
            }

            result = "Permission : " + permissionName + " not found";
            return DataProviderOperationResult.RecordNotFoundError;
        }

        public DataProviderOperationResult DeleteAuthorisation(int authorisationID, out string result)
        {
            var authorisationDBEntity =
                (from rec in DBSecDataContext.Authorisations.ToList() where rec.ID == authorisationID select rec).
                    FirstOrDefault();

            if(authorisationDBEntity!=null)
            {
                DBSecDataContext.Authorisations.DeleteOnSubmit(authorisationDBEntity);
                try
                {
                    DBSecDataContext.SubmitChanges();
                    result = "Authorisation : " + authorisationDBEntity.ID + " [Role : " + authorisationDBEntity.RoleID +
                        " <==> Permission : " + authorisationDBEntity.PermissionID + "] deleted successfully.";
                    return DataProviderOperationResult.Success;
                }
                catch (Exception exception)
                {
                    result = "Exception while deleting Authorisation " + authorisationDBEntity.ID + Environment.NewLine + exception.Message;  
                    return DataProviderOperationResult.UnknownError;
                } 
            }
            result = "Authorisation : " + authorisationID + " not found";
            return DataProviderOperationResult.RecordNotFoundError;
        }

        public bool UpdateRole(IRole role)
        {
            throw new NotImplementedException();
        }

        public bool UpdateUser(IUser user)
        {
            throw new NotImplementedException();
        }

        #endregion
        
        #region Search Entities 
        
        /// <summary>
        /// Find a User record from the DB for the matching UserID
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public IUser FindUser(int userID)
        {
            IUser usr = null;

            User res = (from rec in DBSecDataContext.Users.ToList() where rec.ID == userID select rec).FirstOrDefault();

            if (res != null)
            {
                usr = ConvertToUserEntity(res);
            }
            return usr;
        }

        /// <summary>
        /// Find a User record from the DB for the matching UserName
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public IUser FindUser(string userName)
        {
            IUser usr = null;

            User res =
                (from rec in DBSecDataContext.Users.ToList() where rec.Username == userName select rec).FirstOrDefault();

            if (res != null)
            {
                usr = ConvertToUserEntity(res);
            }
            return usr;
        }
 
        /// <summary>
        /// Find the Role record for the RoleID passed
        /// </summary>
        /// <param name="roleID"></param>
        /// <returns>Returns IRole when result found. Else retuns null.</returns>
        public IRole FindRole(int roleID)
        {
            IRole role = null;

            Role res = (from rec in DBSecDataContext.Roles.ToList() where rec.ID == roleID select rec).FirstOrDefault();

            if (res != null)
            {
                role = ConvertToRoleEntity(res);
            }

            return role;
        }

        public IRole FindRole(string roleName)
        {
            IRole role = null;

            Role res =
                (from rec in DBSecDataContext.Roles.ToList() where rec.RoleName == roleName select rec).FirstOrDefault();

            if (res != null)
            {
                role = ConvertToRoleEntity(res);
            }

            return role;
        }

        public IPermission FindPermission(string permissionName)
        {
            IPermission permission = null;

            var res =
                (from rec in DBSecDataContext.Permissions.ToList() where rec.PermissionName == permissionName select rec).
                    FirstOrDefault();

            if (res != null)
            {
                permission = ConvertToPermissionEntity(res);
            }

            return permission;
        }

        public IPermission FindPermission(int permissionID)
        {
            IPermission permission = null;

            var res =
                (from rec in DBSecDataContext.Permissions.ToList() where rec.ID == permissionID select rec).
                    FirstOrDefault();

            if (res != null)
            {
                permission = ConvertToPermissionEntity(res);
            }

            return permission;
        }

        public IEnumerable<IUser> GetAllUsers()
        {
            var users = new List<IUser>();

            List<User> res = (from rec in DBSecDataContext.Users.ToList() where rec.IsDeleted == false select rec).ToList();
            foreach (User r in res)
            {
                IUser usr = CreateNewUser();
                usr.UserID = r.ID;
                usr.UserName = r.Username;
                usr.Password = r.Password;
                usr.Tel = r.Tel;
                usr.Email = r.Email;
                usr.Department = r.Department;
                usr.Designation = r.Designation;
                usr.FullName = r.FullName;
                usr.IsWindowsUser = r.IsWindowsUser;
                usr.RoleID = r.RoleID;

                users.Add(usr);
            }

            return users;
        }

        public List<IRole> GetAllRoles(bool ignoreDeletedRoles = false)
        {
            var roles = new List<IRole>();
            List<Role> res;

            if (ignoreDeletedRoles)
                res = (from rec in DBSecDataContext.Roles.ToList() select rec).ToList();
            else
                res = (from rec in DBSecDataContext.Roles.ToList() where rec.IsDeleted == false select rec).ToList();

            foreach (Role r in res)
            {
                IRole role = CreateNewRole();
                role.RoleId = r.ID;
                role.RoleName = r.RoleName;
                roles.Add(role);
            }
            return roles;
        }

        public List<IPermission> GetAllPermissions()
        {
            var permissions = new List<IPermission>();
            List<Permission> res = (from rec in DBSecDataContext.Permissions.ToList() select rec).ToList();
            foreach (Permission r in res)
            {
                IPermission permission = CreateNewPermission();
                permission.PermissionID = r.ID;
                permission.PermissionName = r.PermissionName;
                permissions.Add(permission);
            }
            return permissions;
        }

        public List<IPermission> GetAllPermissions(int roleID)
        {
            var permissions = new List<IPermission>();
            List<Authorisation> authorisations =
                (from rec in DBSecDataContext.Authorisations.ToList() where rec.RoleID == roleID select rec).ToList();
            foreach (Authorisation r in authorisations)
            {
                IPermission permission = CreateNewPermission();
                permission.PermissionID = r.ID;
                permission.PermissionName = r.Permission.PermissionName;
                permissions.Add(permission);
            }
            return permissions;
        }

        public List<IPermission> GetAllPermissions(string userName)
        {
            var user = FindUser(userName);
            return GetAllPermissions(user.UserID);
        }

        public List<IPermission> GetAllPermissions(IUser user)
        {
            var permissions = new List<IPermission>();

            if (user != null)
            {
                permissions = GetAllPermissions(ConvertToUserDBEntity(user).Role.ID);
            }

            return permissions;
        }

        public IEnumerable<IAuthorisation> GetAllAuthorisations()
        {
            var authorisationsEntities = new List<IAuthorisation>();
            var authorisationsDBEntities = (from rec in DBSecDataContext.Authorisations.ToList() select rec).ToList();
            foreach (var auth in authorisationsDBEntities)
            {
                var authorisationEntity = CreateNewAuthorisation();
                if (authorisationEntity != null)
                {
                    authorisationEntity.ID = auth.ID;
                    authorisationEntity.RoleID = auth.RoleID;
                    authorisationEntity.PermissionID = auth.PermissionID;
                    authorisationsEntities.Add(authorisationEntity);
                }
            } 
            return authorisationsEntities;
        }

        public List<IAuthorisation>  GetAllAuthorisations(int roleID )
        {
            var authorisationEntities = new List<IAuthorisation>();
            var authorisationDBEntities =
                (from rec in DBSecDataContext.Authorisations.ToList() where rec.RoleID == roleID select rec).ToList();

            foreach (var authorisation in authorisationDBEntities)
            {
                var authorisationEntity = CreateNewAuthorisation();
                if (authorisationEntity != null)
                {
                    authorisationEntity.ID = authorisation.ID;
                    authorisationEntity.RoleID = authorisation.RoleID;
                    authorisationEntity.PermissionID = authorisation.PermissionID;
                    authorisationEntities.Add(authorisationEntity);
                }
            }
            return authorisationEntities;
        }

        public IUserRealm Login(string username, string password)
        {
            if (username == "")
                return null;

            IUserRealm userRealm = new UserRealmBase();

            var userDBEntity =
                (from rec in DBSecDataContext.Users.ToList()
                 where rec.Username == username && rec.Password == password
                 select rec).SingleOrDefault();
            
            userRealm.User = ConvertToUserEntity(userDBEntity);
            return userRealm;
        }
 
        #endregion

        #region Private functions
        /// <summary>
        /// Sets the Role of the UserEntity to Default Role.
        /// </summary>
        /// <param name="user"></param>
        private void SetDefaultRole(User user)
        {
            var defaultRole = FindRole("Default");

            if (defaultRole != null)
                if (defaultRole.RoleId != 0)
                    user.RoleID = defaultRole.RoleId;
        }
        #endregion

        #endregion
        
        #region IDisposable

        public void Dispose()
        {
            DBSecDataContext.Connection.Close();
            DBSecDataContext.Dispose();
            DBSecDataContext = null;
        }

        #endregion

        #region Implementation of IPlugin

        public string Name { get; set; }

        #endregion
    }
}
